using System;
using System.Text;
using System.Collections.Generic;
using System.Collections;

namespace LiveStock.Recommendation
{
    static class StrUtil
    {
        private static readonly char[] WHITE_SPACE = { ' ', '\t', '\n', '\r' };

        /*
         * Cuts a string to only its alpha-characters ([A-Z,a-z])
         * parameter s a string to evaluate
         * return a string with only alpha-characters.
         */
        public static string AlphaOnly(string s)
        {
            StringBuilder sb = new StringBuilder();

            foreach (char c in s.ToCharArray())
                if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
                    sb.Append(c);

            return sb.ToString();
        }

        /*
         * Splits a string into words, denoting WHITE_SPACE as separators
         * parameter text a string to split into words
         * return a string array of the various words
         */
        public static string[] SplitIntoWords(string text)
        {
            string[] result = text.Split(WHITE_SPACE, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < result.Length; i++)
                result[i] = AlphaOnly(result[i]);

            return result;
        }
    }

    class Word
    {
        private readonly string theText;
        private Word nextWord = null;

        public Word NextWord
        {
            get { return nextWord; }
            set { nextWord = value; }
        }

        public Word(string theText)
        {
            this.theText = StrUtil.AlphaOnly(theText);
        }

        /**
         * Determines whether this Word instance holds a match to a
         * particular text
         * param testText a string to compare for a match against this word
         * return a bool stating whether this Word leads to a match
         * against the text
         */
        public bool Matches(string testText)
        {
            if (theText.ToLower().Equals(testText.ToLower()))
                return true;

            string[] words = StrUtil.SplitIntoWords(testText);
            Word testWord = this;

            foreach (string word in words)
            {
                if (testWord == null) return false;
                if (!testWord.theText.ToLower().Equals(word.ToLower())) return false;
                testWord = testWord.NextWord;
            }

            return true;
        }
    }

    public class WordCounter
    {
        private Dictionary<string, List<Word>> wordMap = new Dictionary<string, List<Word>>();

        public WordCounter(string documentText)
        {
            string[] words = StrUtil.SplitIntoWords(documentText);
            Word priorWord = null;

            foreach (string word in words)
            {
                string key = word.ToLower();
                key = StrUtil.AlphaOnly(key);
                List<Word> list = null;

                if (!wordMap.TryGetValue(key, out list))
                {
                    list = new List<Word>();
                    wordMap.Add(key, list);
                }

                Word currentWord = new Word(word);
                list.Add(currentWord);
                if (priorWord != null) priorWord.NextWord = currentWord;
                priorWord = currentWord;
            }
        }

        /**
         * Count the occurences of a particular text
         * parameter text a string for which to count occurances
         * return an int stating the number of occurances
         */
        public int GetCount(string text)
        {
            string key = GenerateKey(text);
            List<Word> list = null;
            int result = 0;

            if (wordMap.TryGetValue(key, out list))
            {
                foreach (Word word in list)
                    if (word.Matches(text)) result++;
            }

            return result;
        }

        /*
         * Generates a key that can be used to access the 
         * proper word-list from the object's dictionary
         * parameter s a string from which to generate a key
         * return a string that can be used as a key
         */
        private string GenerateKey(string s)
        {
            string[] words = StrUtil.SplitIntoWords(s);
            string result = words[0].ToLower();
            return StrUtil.AlphaOnly(result);
        }
    }
}